/************************************************************************************
   Copyright (C) 2020 MariaDB Corporation AB

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not see <http://www.gnu.org/licenses>
   or write to the Free Software Foundation, Inc.,
   51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/


#include "BasePrepareStatement.h"

#include "Results.h"
#include "Parameters.h"

#include "MariaDbStatement.h"
#include "MariaDbConnection.h"
#include "ExceptionFactory.h"

namespace sql
{
namespace mariadb
{

  /**
   * Constructor. Base class that permit setting parameters for client and server PrepareStatement.
   *
   * @param connection current connection
   * @param resultSetScrollType one of the following <code>ResultSet</code> constants: <code>
   *     ResultSet.TYPE_FORWARD_ONLY</code>, <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
   *     <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
   * @param resultSetConcurrency one of the following <code>ResultSet</code> constants: <code>
   *     ResultSet.CONCUR_READ_ONLY</code> or <code>ResultSet.CONCUR_UPDATABLE</code>
   * @param autoGeneratedKeys a flag indicating whether auto-generated keys should be returned; one
   *     of <code>Statement.RETURN_GENERATED_KEYS</code> or <code>Statement.NO_GENERATED_KEYS</code>
   */
  BasePrepareStatement::BasePrepareStatement(
      MariaDbConnection* _connection,
      int32_t resultSetScrollType,
      int32_t resultSetConcurrency,
      int32_t autoGeneratedKeys,
      Shared::ExceptionFactory& factory)
    : stmt(new MariaDbStatement(_connection, resultSetScrollType, resultSetConcurrency, factory)),
      connection(_connection),
      autoGeneratedKeys(autoGeneratedKeys),
      useFractionalSeconds(_connection->getProtocol()->getOptions()->useFractionalSeconds),
      noBackslashEscapes(_connection->getProtocol()->noBackslashEscapes()),
      exceptionFactory(factory),
      protocol(connection->getProtocol().get())
  {
  }


  int64_t BasePrepareStatement::executeLargeUpdate()
  {
    //checkConnectionExists();
    if (executeInternal(getFetchSize())){
      return 0;
    }
    return getLargeUpdateCount();
  }

  /**
   * Sets the designated parameter to the given <code>Reader</code> object, which is the given
   * number of characters long. When a very large UNICODE value is input to a <code>LONGVARCHAR
   * </code> parameter, it may be more practical to send it via a <code>java.io.Reader</code>
   * object. The data will be read from the stream as needed until end-of-file is reached. The JDBC
   * driver will do any necessary conversion from UNICODE to the database char format.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param reader the <code>java.io.Reader</code> object that contains the Unicode data
   * @param length the number of characters in the stream
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
#ifdef MAYBE_IN_NEXTVERSION
  void BasePrepareStatement::setCharacterStream(int32_t parameterIndex, const std::istringstream& reader,int32_t length)
  {
    if (!reader){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new ReaderParameter(reader,length,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to the given <code>Reader</code> object, which is the given
   * number of characters long. When a very large UNICODE value is input to a <code>LONGVARCHAR
   * </code> parameter, it may be more practical to send it via a <code>java.io.Reader</code>
   * object. The data will be read from the stream as needed until end-of-file is reached. The JDBC
   * driver will do any necessary conversion from UNICODE to the database char format.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param reader the <code>java.io.Reader</code> object that contains the Unicode data
   * @param length the number of characters in the stream
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setCharacterStream(int32_t parameterIndex,const std::istringstream& reader,const int64_t length)
  {
    if (reader.){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new ReaderParameter(reader,length,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to the given <code>Reader</code> object. When a very large
   * UNICODE value is input to a <code>LONGVARCHAR</code> parameter, it may be more practical to
   * send it via a <code>java.io.Reader</code> object. The data will be read from the stream as
   * needed until end-of-file is reached. The JDBC driver will do any necessary conversion from
   * UNICODE to the database char format.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
   * efficient to use a version of <code>setCharacterStream</code> which takes a length parameter.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param reader the <code>java.io.Reader</code> object that contains the Unicode data
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setCharacterStream(int32_t parameterIndex,const std::istringstream& reader)
  {
    if (reader.){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new ReaderParameter(reader,noBackslashEscapes));
    hasLongData= true;
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.Blob</code> object. The driver
  * converts this to an SQL <code>BLOB</code> value when it sends it to the database.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param blob a <code>Blob</code> object that maps an SQL <code>BLOB</code> value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */

  void BasePrepareStatement::setBlob(int32_t parameterIndex, const Blob &blob)
  {
    if (blob/*.empty() == true*/) {
      setNull(parameterIndex, Types::BLOB);
      return;
    }
    setParameter(
      parameterIndex,
      new StreamParameter(blob.getBinaryStream(), blob.size(), noBackslashEscapes));
    hasLongData= true;
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.Clob</code> object. The driver
  * converts this to an SQL <code>CLOB</code> value when it sends it to the database.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param clob a <code>Clob</code> object that maps an SQL <code>CLOB</code> value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setClob(int32_t parameterIndex, const Clob&clob)
  {
    if (clob/*.empty() == true*/) {
      setNull(parameterIndex, ColumnType::BLOB);
      return;
    }

    setParameter(
      parameterIndex,
      new ReaderParameter(clob.getCharacterStream(), clob.size(), noBackslashEscapes));
    hasLongData= true;
  }

  /**
  * Sets the designated parameter to a <code>Reader</code> object. The reader must contain the
  * number of characters specified by length otherwise a <code>SQLException</code> will be
  * generated when the <code>PreparedStatement</code> is executed. This method differs from the
  * <code>setCharacterStream (int, Reader,
  * int)</code> method because it informs the driver that the parameter value should be sent to the
  * server as a <code>CLOB</code>. When the <code>setCharacterStream</code> method is used, the
  * driver may have to do extra work to determine whether the parameter data should be sent to the
  * server as a <code>LONGVARCHAR</code> or a <code>CLOB</code>
  *
  * @param parameterIndex index of the first parameter is 1, the second is 2, ...
  * @param reader An object that contains the data to set the parameter value to.
  * @param length the number of characters in the parameter data.
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs; this method is called on a closed <code>
  *     PreparedStatement</code> or if the length specified is less than zero.
  */
  void BasePrepareStatement::setClob(int32_t parameterIndex, const std::istringstream& reader, const int64_t length)
  {
    setCharacterStream(parameterIndex, reader, length);
  }

  /**
  * Sets the designated parameter to a <code>Reader</code> object. This method differs from the
  * <code>setCharacterStream (int, Reader)</code> method because it informs the driver that the
  * parameter value should be sent to the server as a <code>CLOB</code>. When the <code>
  * setCharacterStream</code> method is used, the driver may have to do extra work to determine
  * whether the parameter data should be sent to the server as a <code>LONGVARCHAR</code> or a
  * <code>CLOB</code>
  *
  * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
  * efficient to use a version of <code>setClob</code> which takes a length parameter.
  *
  * @param parameterIndex index of the first parameter is 1, the second is 2, ...
  * @param reader An object that contains the data to set the parameter value to.
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs; this method is called on a closed <code>
  *     PreparedStatement</code>or if parameterIndex does not correspond to a parameter marker in
  *     the SQL statement
  */
  void BasePrepareStatement::setClob(int32_t parameterIndex, const std::istringstream& reader)
  {
    setCharacterStream(parameterIndex, reader);
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.Date</code> value using the default
  * time zone of the virtual machine that is running the application. The driver converts this to
  * an SQL <code>DATE</code> value when it sends it to the database.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param date the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setDate(int32_t parameterIndex, const Date& date)
  {
    if (date/*.empty() == true*/) {
      setNull(parameterIndex, Types::DATE);
      return;
    }
    setParameter(
      parameterIndex, new DateParameter(date, TimeZone.getDefault(), getProtocol()->getOptions()));
  }

 /**
  * Sets the designated parameter to the given <code>java.sql.Time</code> value. the driver uses
  * the default timezone, which is that of the virtual machine running the application.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param time the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setTime(int32_t parameterIndex, const Time&time)
  {
    if (time/*.empty() == true*/) {
      setNull(parameterIndex, ColumnType::TIME);
      return;
    }
    setParameter(
      parameterIndex, new TimeParameter(time, TimeZone.getDefault(), useFractionalSeconds));
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.Timestamp</code> value. The driver
  * converts this to an SQL <code>TIMESTAMP</code> value when it sends it to the database.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param timestamp the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setTimestamp(int32_t parameterIndex, const Timestamp& timestamp)
  {
    if (timestamp.empty()) {
      this->setNull(parameterIndex, ColumnType::DATETIME);
      return;
    }
    setParameter(
      parameterIndex,
      new TimestampParameter(timestamp, getProtocol()->getTimeZone(), useFractionalSeconds));
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.RowId</code> object. The driver
  * converts this to a SQL <code>ROWID</code> value when it sends it to the database
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param rowid the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setRowId(int32_t parameterIndex, const RowId rowid)
  {
    throw exceptionFactory->notSupported("RowIDs not supported");
  }

  /**
  * Sets the designated paramter to the given <code>String</code> object. The driver converts this
  * to a SQL <code>NCHAR</code> or <code>NVARCHAR</code> or <code>LONGNVARCHAR</code> value
  * (depending on the argument's size relative to the driver's limits on <code>NVARCHAR</code>
  * values) when it sends it to the database.
  *
  * @param parameterIndex of the first parameter is 1, the second is 2, ...
  * @param value the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if the driver does not support national character sets; if the driver can detect
  *     that a data conversion error could occur; if a database access error occurs; or this method
  *     is called on a closed <code>PreparedStatement</code>
  */
  void BasePrepareStatement::setNString(int32_t parameterIndex, const SQLString& value)
  {
    setString(parameterIndex, value);
  }

  /**
  * Sets the designated parameter to a <code>Reader</code> object. The <code>Reader</code> reads
  * the data till end-of-file is reached. The driver does the necessary conversion from Java
  * character format to the national character set in the database.
  *
  * @param parameterIndex of the first parameter is 1, the second is 2, ...
  * @param value the parameter value
  * @param length the number of characters in the parameter data.
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if the driver does not support national character sets; if the driver can detect
  *     that a data conversion error could occur; if a database access error occurs; or this method
  *     is called on a closed <code>PreparedStatement</code>
  */
  void BasePrepareStatement::setNCharacterStream(int32_t parameterIndex, const std::istringstream& value, const int64_t length)
  {
    setCharacterStream(parameterIndex, value, length);
  }

  /**
  * Sets the designated parameter to a <code>Reader</code> object. The <code>Reader</code> reads
  * the data till end-of-file is reached. The driver does the necessary conversion from Java
  * character format to the national character set in the database.
  *
  * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
  * subclass that implements the standard interface.
  *
  * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
  * efficient to use a version of <code>setNCharacterStream</code> which takes a length parameter.
  *
  * @param parameterIndex of the first parameter is 1, the second is 2, ...
  * @param value the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if the driver does not support national character sets; if the driver can detect
  *     that a data conversion error could occur; if a database access error occurs; or this method
  *     is called on a closed <code>PreparedStatement</code>
  */
  void BasePrepareStatement::setNCharacterStream(int32_t parameterIndex, const std::istringstream& value)
  {
    setCharacterStream(parameterIndex, value);
  }

  /**
  * Sets the designated parameter to a <code>java.sql.NClob</code> object. The driver converts this
  * to a SQL <code>NCLOB</code> value when it sends it to the database.
  *
  * @param parameterIndex of the first parameter is 1, the second is 2, ...
  * @param value the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if the driver does not support national character sets; if the driver can detect
  *     that a data conversion error could occur; if a database access error occurs; or this method
  *     is called on a closed <code>PreparedStatement</code>
  */
  void BasePrepareStatement::setNClob(int32_t parameterIndex, const NClob&value)
  {
    setClob(parameterIndex, value);
  }

  /**
  * Sets the designated parameter to a <code>Reader</code> object. The reader must contain the
  * number of characters specified by length otherwise a <code>SQLException</code> will be
  * generated when the <code>PreparedStatement</code> is executed. This method differs from the
  * <code>setCharacterStream (int, Reader,
  * int)</code> method because it informs the driver that the parameter value should be sent to the
  * server as a <code>NCLOB</code>. When the <code>setCharacterStream</code> method is used, the
  * driver may have to do extra work to determine whether the parameter data should be sent to the
  * server as a <code>LONGNVARCHAR</code> or a <code>NCLOB</code>
  *
  * @param parameterIndex index of the first parameter is 1, the second is 2, ...
  * @param reader An object that contains the data to set the parameter value to.
  * @param length the number of characters in the parameter data.
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if the length specified is less than zero; if the driver does not support
  *     national character sets; if the driver can detect that a data conversion error could occur;
  *     if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setNClob(int32_t parameterIndex, const std::istringstream& reader, const int64_t length)
  {
    setClob(parameterIndex, reader, length);
  }

  /**
  * Sets the designated parameter to a <code>Reader</code> object. This method differs from the
  * <code>setCharacterStream (int, Reader)</code> method because it informs the driver that the
  * parameter value should be sent to the server as a <code>NCLOB</code>. When the <code>
  * setCharacterStream</code> method is used, the driver may have to do extra work to determine
  * whether the parameter data should be sent to the server as a <code>LONGNVARCHAR</code> or a
  * <code>NCLOB</code>
  *
  * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
  * efficient to use a version of <code>setNClob</code> which takes a length parameter.
  *
  * @param parameterIndex index of the first parameter is 1, the second is 2, ...
  * @param reader An object that contains the data to set the parameter value to.
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if the driver does not support national character sets; if the driver can detect
  *     that a data conversion error could occur; if a database access error occurs or this method
  *     is called on a closed <code>PreparedStatement</code>
  */
  void BasePrepareStatement::setNClob(int32_t parameterIndex, const std::istringstream& reader)
  {
    setClob(parameterIndex, reader);
  }
 #endif

  /**
   * Sets the designated parameter to the given <code>REF(&lt;structured-type&gt;)</code> value. The
   * driver converts this to an SQL <code>REF</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param ref an SQL <code>REF</code> value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
#ifdef JDBC_SPECIFIC_TYPES_IMPLEMENTED
  void BasePrepareStatement::setRef(int32_t parameterIndex,const Ref& ref)
  {
    throw exceptionFactory->notSupported("REF parameter are not supported");
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.Array</code> object. The driver
  * converts this to an SQL <code>ARRAY</code> value when it sends it to the database.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param array an <code>Array</code> object that maps an SQL <code>ARRAY</code> value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setArray(int32_t parameterIndex, const sql::Array&array)
  {
    throw exceptionFactory->notSupported("Arrays not supported");
  }

  /**
  * Sets the designated parameter to the given <code>java.sql.Date</code> value, using the given
  * <code>Calendar</code> object. The driver uses the <code>Calendar</code> object to construct an
  * SQL <code>DATE</code> value, which the driver then sends to the database. With a <code>Calendar
  * </code> object, the driver can calculate the date taking into account a custom timezone. If no
  * <code>Calendar</code> object is specified, the driver uses the default timezone, which is that
  * of the virtual machine running the application.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param date the parameter value
  * @param cal the <code>Calendar</code> object the driver will use to construct the date
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setDate(int32_t parameterIndex, const Date&date, const Calendar* cal)
  {
    if (date/*.empty() == true*/) {
      setNull(parameterIndex, Types::DATE);
      return;
    }
    setParameter(
      parameterIndex,
      new DateParameter(
        date, cal == nullptr ? cal->getTimeZone() : TimeZone::getDefault(), getProtocol()->getOptions()));
  }

 /**
  * Sets the designated parameter to the given <code>java.sql.Time</code> value, using the given
  * <code>Calendar</code> object. The driver uses the <code>Calendar</code> object to construct an
  * SQL <code>TIME</code> value, which the driver then sends to the database. With a <code>Calendar
  * </code> object, the driver can calculate the time taking into account a custom timezone. If no
  * <code>Calendar</code> object is specified, the driver uses the default timezone, which is that
  * of the virtual machine running the application.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param time the parameter value
  * @param cal the <code>Calendar</code> object the driver will use to construct the time
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setTime(int32_t parameterIndex, const Time&time, const Calendar* cal)
  {
    if (time/*.empty() == true*/) {
      setNull(parameterIndex, ColumnType::TIME);
      return;
    }
    setParameter(
      parameterIndex,
      new TimeParameter(
        time, cal/*.empty() == true*/ ? cal.getTimeZone() : TimeZone.getDefault(), useFractionalSeconds));
  }

 /**
  * Sets the designated parameter to the given <code>java.sql.Timestamp</code> value, using the
  * given <code>Calendar</code> object. The driver uses the <code>Calendar</code> object to
  * construct an SQL <code>TIMESTAMP</code> value, which the driver then sends to the database.
  * With a <code>Calendar</code> object, the driver can calculate the timestamp taking into account
  * a custom timezone. If no <code>Calendar</code> object is specified, the driver uses the default
  * timezone, which is that of the virtual machine running the application.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param timestamp the parameter value
  * @param cal the <code>Calendar</code> object the driver will use to construct the timestamp
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setTimestamp(int32_t parameterIndex, const Timestamp& timestamp, const Calendar* cal)
  {
    if (timestamp.empty()) {
      setNull(parameterIndex, ColumnType::DATETIME);
      return;
    }
    TimeZone tz= cal/*.empty() == true*/ ? cal.getTimeZone() : getProtocol()->getTimeZone();
    setParameter(parameterIndex, new TimestampParameter(timestamp, tz, useFractionalSeconds));
  }

  void BasePrepareStatement::setURL(int32_t parameterIndex, const URL& url)
  {
    if (url/*.empty() == true*/) {
      setNull(parameterIndex, ColumnType::STRING);
      return;
    }
    setParameter(parameterIndex, new StringParameter(url/*.toString()*/, noBackslashEscapes));
  }
#endif

  void BasePrepareStatement::setDateTime(int32_t parameterIndex, const SQLString& dt)
  {
    if (dt.empty()) {
      setNull(parameterIndex, ColumnType::DATETIME);
      return;
    }

    //setParameter(parameterIndex, new TimestampParameter(dt, nullptr, useFractionalSeconds));
    setParameter(parameterIndex, new StringParameter(dt, false));
  }

  /**
   * Sets the designated parameter to a <code>InputStream</code> object. The inputstream must
   * contain the number of characters specified by length otherwise a <code>SQLException</code> will
   * be generated when the <code>PreparedStatement</code> is executed. This method differs from the
   * <code>setBinaryStream
   * (int, InputStream, int)</code> method because it informs the driver that the parameter value
   * should be sent to the server as a <code>BLOB</code>. When the <code>setBinaryStream</code>
   * method is used, the driver may have to do extra work to determine whether the parameter data
   * should be sent to the server as a <code>LONGVARBINARY</code> or a <code>BLOB</code>
   *
   * @param parameterIndex index of the first parameter is 1, the second is 2, ...
   * @param inputStream An object that contains the data to set the parameter value to.
   * @param length the number of bytes in the parameter data.
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs; this method is called on a closed <code>
   *     PreparedStatement</code>; if the length specified is less than zero or if the number of
   *     bytes in the inputstream does not match the specfied length.
   */
  void BasePrepareStatement::setBlob(int32_t parameterIndex, std::istream* inputStream, const int64_t length)
  {
    if (inputStream == nullptr){
      setNull(parameterIndex, ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex, new StreamParameter(*inputStream, length, noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to a <code>InputStream</code> object. This method differs from
   * the <code>setBinaryStream (int, InputStream)</code> method because it informs the driver that
   * the parameter value should be sent to the server as a <code>BLOB</code>. When the <code>
   * setBinaryStream</code> method is used, the driver may have to do extra work to determine
   * whether the parameter data should be sent to the server as a <code>LONGVARBINARY</code> or a
   * <code>BLOB</code>
   *
   * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
   * efficient to use a version of <code>setBlob</code> which takes a length parameter.
   *
   * @param parameterIndex index of the first parameter is 1, the second is 2, ...
   * @param inputStream An object that contains the data to set the parameter value to.
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs; this method is called on a closed <code>
   *     PreparedStatement</code> or if parameterIndex does not correspond to a parameter marker in
   *     the SQL statement,
   */
  void BasePrepareStatement::setBlob(int32_t parameterIndex, std::istream* inputStream)
  {
    if (inputStream == nullptr){
      setNull(parameterIndex, ColumnType::BLOB);
      return;
    }

    setParameter(parameterIndex, new StreamParameter(*inputStream, noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to SQL <code>NULL</code>.
   *
   * <p><B>Note:</B> You must specify the parameter's SQL type.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param sqlType the SQL type code defined in <code>java.sql.Types</code>
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setNull(int32_t parameterIndex, int32_t /*sqlType*/)
  {
    setParameter(parameterIndex, new NullParameter());
  }

  /**
   * Sets the designated parameter to SQL <code>NULL</code>.
   *
   * <p><B>Note:</B> You must specify the parameter's SQL type.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param mariadbType the type code defined in <code> ColumnType</code>
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setNull(int32_t parameterIndex,const ColumnType& mariadbType)
  {
    setParameter(parameterIndex,new NullParameter(mariadbType));
  }

  /**
   * Sets the designated parameter to SQL <code>NULL</code>. This version of the method <code>
   * setNull</code> should be used for user-defined types and REF type parameters. Examples of
   * user-defined types include: STRUCT, DISTINCT, JAVA_OBJECT, and named array types.
   *
   * <p><B>Note:</B> To be portable, applications must give the SQL type code and the
   * fully-qualified SQL type name when specifying a NULL user-defined or REF parameter. In the case
   * of a user-defined type the name is the type name of the parameter itself. For a REF parameter,
   * the name is the type name of the referenced type. If a JDBC driver does not need the type code
   * or type name information, it may ignore it.
   *
   * <p>Although it is intended for user-defined and Ref parameters, this method may be used to set
   * a null parameter of any JDBC type. If the parameter does not have a user-defined or REF type,
   * the given typeName is ignored.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param sqlType a value from <code>java.sql.Types</code>
   * @param typeName the fully-qualified name of an SQL user-defined type; ignored if the parameter
   *     is not a user-defined type or REF
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setNull(int32_t parameterIndex, int32_t /*sqlType*/, const SQLString& /*typeName*/)
  {
    setParameter(parameterIndex, new NullParameter());
  }

#ifdef JDBC_SPECIFIC_TYPES_IMPLEMENTED
  void BasePrepareStatement::setSQLXML(int32_t parameterIndex, const SQLXML& xmlObject)
  {
    throw exceptionFactory->notSupported("SQLXML not supported");
  }

  /**
   * Sets the value of the designated parameter with the given object. The second argument must be
   * an object type; for integral values, the <code>java.lang</code> equivalent objects should be
   * used.
   *
   * <p>If the second argument is an <code>InputStream</code> then the stream must contain the
   * number of bytes specified by scaleOrLength. If the second argument is a <code>Reader</code>
   * then the reader must contain the number of characters specified by scaleOrLength. If these
   * conditions are not true the driver will generate a <code>SQLException</code> when the prepared
   * statement is executed.
   *
   * <p>The given Java object will be converted to the given targetSqlType before being sent to the
   * database.
   *
   * <p>If the object has a custom mapping (is of a class implementing the interface <code>SQLData
   * </code>), the JDBC driver should call the method <code>SQLData.writeSQL</code> to write it to
   * the SQL data stream. If, on the other hand, the object is of a class implementing <code>Ref
   * </code>, <code>Blob</code>, <code>Clob</code>, <code>NClob</code>, <code>Struct</code>, <code>
   * java.net.URL</code>, or <code>Array</code>, the driver should pass it to the database as a
   * value of the corresponding SQL type.
   *
   * <p>Note that this method may be used to pass database-specific abstract data types.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param obj the object containing the input parameter value
   * @param targetSqlType the SQL type (as defined in java.sql.Types) to be sent to the database.
   *     The scale argument may further qualify this type.
   * @param scaleOrLength for <code>java.sql.Types.DECIMAL</code> or <code>java.sql.Types.NUMERIC
   *                       types</code>, this is the number of digits after the decimal point. For
   *     Java Object types <code>InputStream</code> and <code>Reader</code>, this is the length of
   *     the data in the stream or reader. For all other types, this value will be ignored.
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs; this method is called on a closed <code>
   *     PreparedStatement</code> or if the Java Object specified by x is an InputStream or Reader
   *     object and the value of the scale parameter is less than zero
   * @see Types
   */
  void BasePrepareStatement::setObject( int32_t parameterIndex, const sql::Object&obj,int32_t targetSqlType,int32_t scaleOrLength)
  {
    setInternalObject(parameterIndex,obj,targetSqlType,scaleOrLength);
  }

  /**
   * Sets the value of the designated parameter with the given object. This method is like the
   * method <code>setObject</code> above, except that it assumes a scale of zero.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param obj the object containing the input parameter value
   * @param targetSqlType the SQL type (as defined in java.sql.Types) to be sent to the database
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>s
   * @see Types
   */
  void BasePrepareStatement::setObject(int32_t parameterIndex, const sql::Object&obj, int32_t targetSqlType)
  {
    setInternalObject(parameterIndex,obj,targetSqlType,int64_t.MAX_VALUE);
  }

  /**
   * Sets the value of the designated parameter using the given object. The second parameter must be
   * of type <code>Object</code>; therefore, the <code>java.lang</code> equivalent objects should be
   * used for built-in types.
   *
   * <p>The JDBC specification specifies a standard mapping from Java <code>Object</code> types to
   * SQL types. The given argument will be converted to the corresponding SQL type before being sent
   * to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param obj the object containing the input parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs; this method is called on a closed <code>
   *     PreparedStatement</code> or the type of the given object is ambiguous
   */
  void BasePrepareStatement::setObject(int32_t parameterIndex, const sql::Object&obj)
  {
    if (obj/*.empty() == true*/){
      setNull(parameterIndex,Types::INTEGER);
    }else if (INSTANCEOF(obj, SQLString)){
      setString(parameterIndex,(SQLString)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setInt(parameterIndex,static_cast<int32_t>(obj);)
    }else if (INSTANCEOF(obj, SQLString)){
      setLong(parameterIndex,static_cast<int32_t>(obj);)
    }else if (INSTANCEOF(obj, SQLString)){
      setShort(parameterIndex,static_cast<int32_t>(obj);)
    }else if (INSTANCEOF(obj, SQLString)){
      setDouble(parameterIndex,(Double)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setFloat(parameterIndex,(Float)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setByte(parameterIndex,(Byte)obj);
    }else if (INSTANCEOF(obj, SQLString)::bytes){
      setBytes(parameterIndex,(sql::bytes)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setDate(parameterIndex,(Date)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setTime(parameterIndex,(Time)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setTimestamp(parameterIndex,(Timestamp)obj);
    }else if (INSTANCEOF(obj, SQLString).util.Date){
      setTimestamp(parameterIndex,new Timestamp(((java.util.Date)obj).getTime()));
    }else if (INSTANCEOF(obj, SQLString)){
      setBoolean(parameterIndex,(Boolean)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setBlob(parameterIndex,(Blob)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setBinaryStream(parameterIndex,(InputStream)obj);
    }else if (INSTANCEOF(obj, SQLString)::istringstream&){
      setCharacterStream(parameterIndex,(std::istringstream&)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setBigDecimal(parameterIndex,(BigDecimal)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setString(parameterIndex,obj/*.toString()*/);
    }else if (INSTANCEOF(obj, SQLString)){
      setClob(parameterIndex,(Clob)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setTimestamp(parameterIndex,Timestamp.valueOf((LocalDateTime)obj));
    }else if (INSTANCEOF(obj, SQLString)){
      setTimestamp(parameterIndex,Timestamp.from((Instant)obj));
    }else if (INSTANCEOF(obj, SQLString)){
      setDate(parameterIndex,Date.valueOf((LocalDate)obj));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(
          parameterIndex,
          new ZonedDateTimeParameter(
            ((OffsetDateTime)obj).toZonedDateTime(),
            getProtocol()->getTimeZone().toZoneId(),
            useFractionalSeconds,
            options));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(
          parameterIndex,
          new OffsetTimeParameter(
            (OffsetTime)obj,getProtocol()->getTimeZone().toZoneId(),useFractionalSeconds,options));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(
          parameterIndex,
          new ZonedDateTimeParameter(
            (ZonedDateTime)obj,
            getProtocol()->getTimeZone().toZoneId(),
            useFractionalSeconds,
            options));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(parameterIndex,new LocalTimeParameter((LocalTime)obj,useFractionalSeconds));
    }else {

      setParameter(parameterIndex,new SerializableParameter(obj,noBackslashEscapes));
      hasLongData= true;
    }
  }

  void BasePrepareStatement::setObject(int32_t parameterIndex, const sql::Object&obj,const SQLType&targetSqlType,int32_t scaleOrLength)
  {
    setObject(parameterIndex,obj,targetSqlType.getVendorTypeNumber(),scaleOrLength);
  }

  void BasePrepareStatement::setObject(int32_t parameterIndex, const sql::Object&obj,const SQLType&targetSqlType)
  {
    setObject(parameterIndex,obj,targetSqlType.getVendorTypeNumber());
  }

  void BasePrepareStatement::setInternalObject( int32_t parameterIndex, const sql::Object* obj, int32_t targetSqlType,const int64_t scaleOrLength)
  {
    switch (targetSqlType){
      case Types::ARRAY:
      case Types::DATALINK:
      case Types::JAVA_OBJECT:
      case Types::REF:
      case Types::ROWID:
      case Types::SQLXML:
      case Types::STRUCT:
        throw exceptionFactory->notSupported("Type not supported");
      default:
        break;
    }

    if (obj/*.empty() == true*/){
      setNull(parameterIndex,Types::INTEGER);
    }else if (INSTANCEOF(obj, SQLString)){
      if (targetSqlType == Types::BLOB){
        throw *exceptionFactory->create("Cannot convert a SQLString to a Blob");
      }
      SQLString str= (SQLString)obj;
      try {
        switch (targetSqlType){
          case Types::BIT:
          case Types::BOOLEAN:
            setBoolean(parameterIndex,!("false".equalsIgnoreCase(str)||));
            break;
          case Types::TINYINT:
            setByte(parameterIndex,Byte.parseByte(str));
            break;
          case Types::SMALLINT:
            setShort(parameterIndex,int16_t.parseShort(str));
            break;
          case Types::INTEGER:
            setInt(parameterIndex,int32_t.parseInt(str));
            break;
          case Types::DOUBLE:
          case Types::FLOAT:
            setDouble(parameterIndex,Double.valueOf(str));
            break;
          case Types::REAL:
            setFloat(parameterIndex,Float.valueOf(str));
            break;
          case Types::BIGINT:
            setLong(parameterIndex,int64_t.valueOf(str));
            break;
          case Types::DECIMAL:
          case Types::NUMERIC:
            setBigDecimal(parameterIndex,new BigDecimal(str));
            break;
          case Types::CLOB:
          case Types::NCLOB:
          case Types::CHAR:
          case Types::VARCHAR:
          case Types::LONGVARCHAR:
          case Types::NCHAR:
          case Types::NVARCHAR:
          case Types::LONGNVARCHAR:
            setString(parameterIndex,str);
            break;
          case Types::TIMESTAMP:
            if (str.startsWith("0000-00-00")){
              setTimestamp(parameterIndex, nullptr);
            }else {
              setTimestamp(parameterIndex, Timestamp.valueOf(str));
            }
            break;
          case Types::TIME:
            setTime(parameterIndex,Time.valueOf((SQLString)obj));
            break;
          case Types::TIME_WITH_TIMEZONE:
            setParameter(
                parameterIndex,
                new OffsetTimeParameter(
                  OffsetTime::parse(str),
                  getProtocol()->getTimeZone().toZoneId(),
                  useFractionalSeconds,
                  options));
            break;
          case Types::TIMESTAMP_WITH_TIMEZONE:
            setParameter(
                parameterIndex,
                new ZonedDateTimeParameter(
                  ZonedDateTime::parse(str,SPEC_ISO_ZONED_DATE_TIME),
                  getProtocol()->getTimeZone().toZoneId(),
                  useFractionalSeconds,
                  options));
            break;
          default:
            throw *exceptionFactory->create(
                "Could not convert ["+str +"] to "+targetSqlType);
        }
      }catch (std::exception& e){
        throw *exceptionFactory->create(
            "Could not convert ["+str +"] to "+targetSqlType,e);
      }
    }else if (INSTANCEOF(obj, SQLString)){
      Number bd= (Number)obj;
      switch (targetSqlType){
        case Types::TINYINT:
          setByte(parameterIndex,bd.byteValue());
          break;
        case Types::SMALLINT:
          setShort(parameterIndex,bd.shortValue());
          break;
        case Types::INTEGER:
          setInt(parameterIndex,bd.intValue());
          break;
        case Types::BIGINT:
          setLong(parameterIndex,bd.longValue());
          break;
        case Types::FLOAT:
        case Types::DOUBLE:
          setDouble(parameterIndex,bd.doubleValue());
          break;
        case Types::REAL:
          setFloat(parameterIndex,bd.floatValue());
          break;
        case Types::DECIMAL:
        case Types::NUMERIC:
          if (INSTANCEOF(obj, SQLString)){
            setBigDecimal(parameterIndex,(BigDecimal)obj);
          }else if (INSTANCEOF(obj, SQLString) ||INSTANCEOF(obj, SQLString)){
            setDouble(parameterIndex,bd.doubleValue());
          }else {
            setLong(parameterIndex,bd.longValue());
          }
          break;
        case Types::BIT:
          setBoolean(parameterIndex,bd.shortValue()!=0);
          break;
        case Types::CHAR:
        case Types::VARCHAR:
          setString(parameterIndex,bd/*.toString()*/);
          break;
        default:
          throw *exceptionFactory->create(
              "Could not convert ["+bd +"] to "+targetSqlType);
      }
    }else if (INSTANCEOF(obj, sql::bytes){
      if (targetSqlType == Types::BINARY
          ||targetSqlType == Types::VARBINARY
          ||targetSqlType == Types::LONGVARBINARY){
        setBytes(parameterIndex,(sql::bytes)obj);
      }else {
        throw *exceptionFactory->create(
            "Can only convert a sql::bytes to BINARY, VARBINARY or LONGVARBINARY");
      }

    }else if (INSTANCEOF(obj, SQLString)){
      setTime(parameterIndex,(Time)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setTimestamp(parameterIndex,(Timestamp)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setDate(parameterIndex,(Date)obj);
    }else if (INSTANCEOF(obj, SQLString).util.Date){
      int64_t timemillis= ((java.util.Date)obj).getTime();
      if (targetSqlType == Types::DATE){
        setDate(parameterIndex,new Date(timemillis));
      }else if (targetSqlType == Types::TIME){
        setTime(parameterIndex,new Time(timemillis));
      }else if (targetSqlType == Types::TIMESTAMP){
        setTimestamp(parameterIndex,new Timestamp(timemillis));
      }
    }else if (INSTANCEOF(obj, SQLString)){
      setBoolean(parameterIndex,(Boolean)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setBlob(parameterIndex,(Blob)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setClob(parameterIndex,(Clob)obj);
    }else if (INSTANCEOF(obj, SQLString)){
      setBinaryStream(parameterIndex,(InputStream)obj,scaleOrLength);
    }else if (INSTANCEOF(obj, SQLString)::istringstream&){
      setCharacterStream(parameterIndex,(std::istringstream&)obj,scaleOrLength);
    }else if (INSTANCEOF(obj, SQLString)){
      setTimestamp(parameterIndex,Timestamp.valueOf((LocalDateTime)obj));
    }else if (INSTANCEOF(obj, SQLString)){
      setTimestamp(parameterIndex,Timestamp.from((Instant)obj));
    }else if (INSTANCEOF(obj, SQLString)){
      setDate(parameterIndex,Date.valueOf((LocalDate)obj));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(
          parameterIndex,
          new ZonedDateTimeParameter(
            ((OffsetDateTime)obj).toZonedDateTime(),
            getProtocol()->getTimeZone().toZoneId(),
            useFractionalSeconds,
            options));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(
          parameterIndex,
          new OffsetTimeParameter(
            (OffsetTime)obj,getProtocol()->getTimeZone().toZoneId(),useFractionalSeconds,options));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(
          parameterIndex,
          new ZonedDateTimeParameter(
            (ZonedDateTime)obj,
            getProtocol()->getTimeZone().toZoneId(),
            useFractionalSeconds,
            options));
    }else if (INSTANCEOF(obj, SQLString)){
      setParameter(parameterIndex,new LocalTimeParameter((LocalTime)obj,useFractionalSeconds));
    }else {
      throw *exceptionFactory->create(
          "Could not set parameter in setObject, could not convert: "
          +obj.getClass()
          +" to "
          +targetSqlType);
    }
  }

  /**
  * Sets the designated parameter to the given <code>java.math.BigDecimal</code> value. The driver
  * converts this to an SQL <code>NUMERIC</code> value when it sends it to the database.
  *
  * @param parameterIndex the first parameter is 1, the second is 2, ...
  * @param bigDecimal the parameter value
  * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
  *     statement; if a database access error occurs or this method is called on a closed <code>
  *     PreparedStatement</code>
  */
  void BasePrepareStatement::setBigDecimal(int32_t parameterIndex, const BigDecimal bigDecimal)
  {
    if (bigDecimal/*.empty() == true*/) {
      setNull(parameterIndex, ColumnType::DECIMAL);
      return;
    }

    setParameter(parameterIndex, new BigDecimalParameter(bigDecimal));
  }
#endif

#ifdef MAYBE_IN_NEXTVERSION
  /**
   * Sets the designated parameter to the given input stream, which will have the specified number
   * of bytes. When a very large ASCII value is input to a <code>LONGVARCHAR</code> parameter, it
   * may be more practical to send it via a <code>java.io.InputStream</code>. Data will be read from
   * the stream as needed until end-of-file is reached. The JDBC driver will do any necessary
   * conversion from ASCII to the database char format.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param stream the Java input stream that contains the ASCII parameter value
   * @param length the number of bytes in the stream
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setAsciiStream(int32_t parameterIndex, const std::istream&stream,const int64_t length)
  {
    if (stream/*.empty() == true*/){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new StreamParameter(stream,length,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * This function reads up the entire stream and stores it in memory since we need to know the
   * length when sending it to the server use the corresponding method with a length parameter if
   * memory is an issue <br>
   * Sets the designated parameter to the given input stream. When a very large ASCII value is input
   * to a <code>LONGVARCHAR</code> parameter, it may be more practical to send it via a <code>
   * java.io.InputStream</code>. Data will be read from the stream as needed until end-of-file is
   * reached. The JDBC driver will do any necessary conversion from ASCII to the database char
   * format.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
   * efficient to use a version of <code>setAsciiStream</code> which takes a length parameter.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param stream the Java input stream that contains the ASCII parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setAsciiStream(int32_t parameterIndex, const std::istream&stream)
  {
    if (stream/*.empty() == true*/){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new StreamParameter(stream,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to the given input stream, which will have the specified number
   * of bytes. When a very large ASCII value is input to a <code>LONGVARCHAR</code> parameter, it
   * may be more practical to send it via a <code>java.io.InputStream</code>. Data will be read from
   * the stream as needed until end-of-file is reached. The JDBC driver will do any necessary
   * conversion from ASCII to the database char format.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param stream the Java input stream that contains the ASCII parameter value
   * @param length the number of bytes in the stream
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setAsciiStream(int32_t parameterIndex, const std::istream&stream,int32_t length)
  {
    if (stream/*.empty() == true*/){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new StreamParameter(stream,length,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to the given input stream, which will have the specified number
   * of bytes. When a very large binary value is input to a <code>LONGVARBINARY</code> parameter, it
   * may be more practical to send it via a <code>java.io.InputStream</code> object. The data will
   * be read from the stream as needed until end-of-file is reached.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param stream the java input stream which contains the binary parameter value
   * @param length the number of bytes in the stream
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setBinaryStream(int32_t parameterIndex, const std::istream&stream,const int64_t length)
  {
    if (stream/*.empty() == true*/){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new StreamParameter(stream,length,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * This function reads up the entire stream and stores it in memory since we need to know the
   * length when sending it to the server <br>
   * Sets the designated parameter to the given input stream. When a very large binary value is
   * input to a <code>LONGVARBINARY</code> parameter, it may be more practical to send it via a
   * <code>java.io.InputStream</code> object. The data will be read from the stream as needed until
   * end-of-file is reached.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * <p><B>Note:</B> Consult your JDBC driver documentation to determine if it might be more
   * efficient to use a version of <code>setBinaryStream</code> which takes a length parameter.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param stream the java input stream which contains the binary parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setBinaryStream(int32_t parameterIndex, const std::istream&stream)
  {
    if (stream/*.empty() == true*/){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new StreamParameter(stream,noBackslashEscapes));
    hasLongData= true;
  }

  /**
   * Sets the designated parameter to the given input stream, which will have the specified number
   * of bytes. When a very large binary value is input to a <code>LONGVARBINARY</code> parameter, it
   * may be more practical to send it via a <code>java.io.InputStream</code> object. The data will
   * be read from the stream as needed until end-of-file is reached.
   *
   * <p><B>Note:</B> This stream object can either be a standard Java stream object or your own
   * subclass that implements the standard interface.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param stream the java input stream which contains the binary parameter value
   * @param length the number of bytes in the stream
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setBinaryStream(int32_t parameterIndex, const std::istream&stream,int32_t length)
  {
    if (stream/*.empty() == true*/){
      setNull(parameterIndex,ColumnType::BLOB);
      return;
    }
    setParameter(parameterIndex,new StreamParameter(stream,length,noBackslashEscapes));
    hasLongData= true;
  }
#endif

  /**
   * Sets the designated parameter to the given Java <code>boolean</code> value. The driver converts
   * this to an SQL <code>BIT</code> or <code>BOOLEAN</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param value the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setBoolean(int32_t parameterIndex, bool value)
  {
    setParameter(parameterIndex,new BooleanParameter(value));
  }

  /**
   * Sets the designated parameter to the given Java <code>byte</code> value. The driver converts
   * this to an SQL <code>TINYINT</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param bit the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setByte(int32_t parameterIndex, int8_t bit)
  {
    setParameter(parameterIndex,new ByteParameter(bit));
  }

  /**
   * Sets the designated parameter to the given Java <code>short</code> value. The driver converts
   * this to an SQL <code>SMALLINT</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param value the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setShort(int32_t parameterIndex,const int16_t value)
  {
    setParameter(parameterIndex,new ShortParameter(value));
  }

  /**
   * Set string parameter.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param str String
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setString(int32_t parameterIndex, const SQLString& str)
  {
    /*if (str == nullptr){
      setNull(parameterIndex,ColumnType::VARCHAR);
      return;
    }*/

    setParameter(parameterIndex,new StringParameter(str, noBackslashEscapes));
  }

  /**
   * Sets the designated parameter to the given Java array of bytes. The driver converts this to an
   * SQL <code>VARBINARY</code> or <code>LONGVARBINARY</code> (depending on the argument's size
   * relative to the driver's limits on <code>VARBINARY</code> values) when it sends it to the
   * database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param bytes the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setBytes(int32_t parameterIndex, sql::bytes* bytes)
  {
    if (bytes == nullptr){
      setNull(parameterIndex, ColumnType::BLOB);
      return;
    }

    setParameter(parameterIndex, new ByteArrayParameter(*bytes, noBackslashEscapes));
  }

  void BasePrepareStatement::setInt(int32_t column, int32_t value)
  {
    setParameter(column, new IntParameter(value));
  }

  /**
   * Sets the designated parameter to the given Java <code>long</code> value. The driver converts
   * this to an SQL <code>BIGINT</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param value the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setLong(int32_t parameterIndex, int64_t value) {
    setParameter(parameterIndex, new LongParameter(value));
  }


  void BasePrepareStatement::setUInt64(int32_t parameterIndex, uint64_t value) {
    setParameter(parameterIndex, new ULongParameter(value));
  }


  void BasePrepareStatement::setUInt(int32_t parameterIndex, uint32_t value) {
    setParameter(parameterIndex, new ULongParameter(value));
  }


  void BasePrepareStatement::setBigInt(int32_t parameterIndex, const SQLString& str) {
    /*if (str == nullptr){
      setNull(parameterIndex,ColumnType::VARCHAR);
      return;
    }*/

    setParameter(parameterIndex, new StringParameter(str, noBackslashEscapes));
  }

  /**
   * Sets the designated parameter to the given Java <code>float</code> value. The driver converts
   * this to an SQL <code>REAL</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param value the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setFloat(int32_t parameterIndex, float value)
  {
    setParameter(parameterIndex,new FloatParameter(value));
  }

  /**
   * Sets the designated parameter to the given Java <code>double</code> value. The driver converts
   * this to an SQL <code>DOUBLE</code> value when it sends it to the database.
   *
   * @param parameterIndex the first parameter is 1, the second is 2, ...
   * @param value the parameter value
   * @throws SQLException if parameterIndex does not correspond to a parameter marker in the SQL
   *     statement; if a database access error occurs or this method is called on a closed <code>
   *     PreparedStatement</code>
   */
  void BasePrepareStatement::setDouble(int32_t parameterIndex, double value)
  {
    setParameter(parameterIndex,new DoubleParameter(value));
  }


  bool BasePrepareStatement::execute()
  {
    return executeInternal(getFetchSize());
  }

  /**
    * Executes the SQL query in this <code>PreparedStatement</code> object and returns the <code>
    * ResultSet</code> object generated by the query.
    *
    * @return a <code>ResultSet</code> object that contains the data produced by the query; never
    *     <code>null</code>
    * @throws SQLException if a database access error occurs; this method is called on a closed
    *     <code>PreparedStatement</code> or the SQL statement does not return a <code>ResultSet
    *     </code> object
    */
  ResultSet* BasePrepareStatement::executeQuery()
  {
    if (execute()) {
      return stmt->getInternalResults()->releaseResultSet();
    }
    return SelectResultSet::createEmptyResultSet();
  }


  int32_t BasePrepareStatement::executeUpdate()
  {
    if (execute()) {
      return 0;
    }
    return getUpdateCount();
  }


  /**
   * Reset timeout after query, re-throw SQL exception.
   *
   * @param sqle current exception
   * @return SQLException exception with new message in case of timer timeout.
   */
  MariaDBExceptionThrower BasePrepareStatement::executeExceptionEpilogue(SQLException& sqle)
  {
    if (!sqle.getSQLState().empty() && sqle.getSQLState().startsWith("08")) {
      try {
        close();
      }
      catch (SQLException&) {

      }
    }

    return stmt->executeExceptionEpilogue(sqle);
  }

  /*** Inherited Statement methods that should not be used in Prepared/CallableStatement. We throw the exception here ***/

  void BasePrepareStatement::addBatch(const SQLString& /*sql*/) {
    exceptionFactory->create("addBatch(const SQString& sql) cannot be called on PreparedStatement").Throw();
  }

  int32_t BasePrepareStatement::executeUpdate(const SQLString& /*sql*/) {
    exceptionFactory->create("executeUpdate(const SQString& sql) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int32_t BasePrepareStatement::executeUpdate(const SQLString& /*sql*/, int32_t /*autoGeneratedKeys*/)  {
    exceptionFactory->create("executeUpdate(const SQString& sql, int32_t autoGeneratedKeys) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int32_t BasePrepareStatement::executeUpdate(const SQLString& /*sql*/, int32_t* /*columnIndexes*/) {
    exceptionFactory->create("executeUpdate(const SQString& sql, int32_t* columnIndexes) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int32_t BasePrepareStatement::executeUpdate(const SQLString& /*sql*/, const SQLString* /*columnNames*/) {
    exceptionFactory->create("executeUpdate(const SQString& sql, const SQLString* columnNames) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int64_t BasePrepareStatement::executeLargeUpdate(const SQLString& /*sql*/) {
    exceptionFactory->create("executeLargeUpdate(const SQString& sql) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int64_t BasePrepareStatement::executeLargeUpdate(const SQLString& /*sql*/, int32_t /*autoGeneratedKeys*/) {
    exceptionFactory->create("executeLargeUpdate(const SQString& sql, int32_t autoGeneratedKeys) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int64_t BasePrepareStatement::executeLargeUpdate(const SQLString& /*sql*/, int32_t* /*columnIndexes*/) {
    exceptionFactory->create("executeLargeUpdate(const SQString& sql, int32_t* columnIndexes) cannot be called on PreparedStatement").Throw();
    return 0;
  }

  int64_t BasePrepareStatement::executeLargeUpdate(const SQLString& /*sql*/, const SQLString* /*columnNames*/) {
    exceptionFactory->create("executeLargeUpdate(const SQString& sql), SQLString* columnNames cannot be called on PreparedStatement").Throw();
    return 0;
  }

  ResultSet* BasePrepareStatement::executeQuery(const SQLString& /*sql*/) {
    exceptionFactory->create("executeQuery(const SQString& sql) cannot be called on PreparedStatement").Throw();
    return nullptr;
  }

  bool BasePrepareStatement::execute(const SQLString& /*sql*/) {
    exceptionFactory->create("execute(const SQString& sql) cannot be called on PreparedStatement").Throw();
    return false;
  }

  bool BasePrepareStatement::execute(const SQLString& /*sql*/, int32_t /*autoGeneratedKeys*/) {
    exceptionFactory->create("execute(const SQString& sql, int32_t autoGeneratedKeys) cannot be called on PreparedStatement").Throw();
    return false;
  }

  bool BasePrepareStatement::execute(const SQLString& /*sql*/, int32_t* /*columnIndexes*/) {
    exceptionFactory->create("execute(const SQString& sql, int32_t* columnIndexes) cannot be called on PreparedStatement").Throw();
    return false;
  }

  bool BasePrepareStatement::execute(const SQLString& /*sql*/, const SQLString* /*columnNames*/) {
    exceptionFactory->create("execute(const SQString& sql, const SQLString* columnNames) cannot be called on PreparedStatement").Throw();
    return false;
  }
}
}
